/////////////////////////////////////////////////////////////////////////
//
// Amuse Engine SDK - system/io
// Copyright (c) 2014.  All Rights Reserved
//
// File:		AEFileManager.h
// Author:		Gianluca Belardelli
// Date:		10/10/2014
//
/////////////////////////////////////////////////////////////////////////
#ifndef _AEFILEMANAGER_H_
#define _AEFILEMANAGER_H_

class AESearchPath;

class AEFileManager;
typedef AESmartPtr<AEFileManager> AEFileManagerPtr;

class AE_DLLIMPEXP AEFileManager : public AEReferencedObject
{
// Members
private:
	static AEFileManagerPtr	ms_fmInstance;

	mutable AECriticalSection m_csMutex;

	AETArray< AESmartPtr<AEFileSystemFactoryI> > m_arrFileSystemFactories;
	AETArray< AESmartPtr<AESearchPath> > m_arrSearchPaths;

	AEStringMap< AEUINT32 > m_mapRoots;

public:
	/// Constants used within the AEFileManager class
	enum AEFileConstants
	{
		AE_MAX_ROOTNAME_LEN = 64 ///< Lunghezza massima consentita per il nome della root
	};

// Methods
protected:
	AEFileManager( void );

public:
	AE_DECLARE_PLACEMENT_ALLOCATOR();
	/// \brief
	///   Returns the global instance of the VFileAccessManager; asserts if the instance has not been created
	///   by a call to Init() yet.
	static AEFileManager *GetInstance( void );

	/// \brief
	///   Creates the global VFileAccessManager instance.
	static void Init( void );

	/// \brief
	///   Destroys the global VFileAccessManager instance. Note that calls to Init() and DeInit() are not
	///   reference counted and thus need not be balanced.
	static void DeInit( void );

	/// \brief
	///   Returns whether the global VFileAccessManager instance has been created.
	static AEBOOL32 IsInitialized( void );

	/// @name File System Factory Management
	/// @{

	/// \brief
	///   Registers an AEFileSystemFactoryI with this VFileAccessManager.
	/// \param pFactory
	///   the IVFileSystemFactory to register. If it is already registered, it won't be
	///   registered again.
	void AddFileSystemFactory( AEFileSystemFactoryI *lpFactory );

	/// \brief
	///   Removes the specified IVFileSystemFactory from this VFileAccessManager.
	/// \param iIndex
	///   index of the entry to remove; must be within the valid range
	void RemoveFileSystemFactory( AEUINT32 uiIndex );

	/// \brief
	///   Removes all file system factories currently registered with this VFileAccessManager.
	void ClearFileSystemFactories( void );

	/// \brief
	///   Returns the index of \c pFactory, or \c -1 if \c pFactory is not registered with this
	///    VFileAccessManager.
	AEINT32 GetIndexOfFileSystemFactory( AEFileSystemFactoryI *lpFactory ) const;

	/// \brief
	///   Returns the number of registered file system factories.
	AEUINT32 GetNumFileSystemFactories( void ) const;

	/// \brief
	///   Returns the requested file system factory.
	/// \param iIndex
	///   index of the entry to remove; must be within the valid range
	/// \return
	///   the requested file system factory
	AEFileSystemFactoryI *GetFileSystemFactory( AEUINT32 uiIndex ) const;

	/// @}

	///
	/// @name File Access Configuration Functions
	/// @{
	///

	/// \brief
	///   Adds a file system to be used by the engine for file operations.
	///
	/// This function operates on the global VFileAccessManager.
	///
	/// A special notation is required for paths that are located within an archive or a package (.zip, .v, 
	/// .vpk, .zip.sdat, .v.sdat, .vpk.sdat). In this case, the question mark character \c ? is used
	/// to separate the path to the package from the path within the package. 
	/// For example:
	/// \verbatim main.zip?baseData \endverbatim
	/// 
	/// \param szRootName
	///   name of the root under which to add the file system
	/// \param szBasePath
	///   base path of the file system to add. This can be an absolute path, or a path relative to the
	///   current directory.
	/// \param uiFlags
	///   flags controlling the detailed behavior of this function. See VFileSystemFlags.
	/// \return
	///   whether adding the file system was successful
	/// \see VFileAccessManager::SetRoot
	AEBOOL32 AddFileSystem( const char *lpRootName, const char *lpBasePath, AEUINT32 uiFlags = 0 );

	/// \brief
	///   Adds a file system to be used by the engine for file operations.
	///
	/// This function operates on the global VFileAccessManager.
	///
	/// \param szRootName
	///   name of the root under which to add the file system
	/// \param pFileSystem
	///   the file system to add. May be NULL, in which case the call is equivalent to RemoveFileSystem.
	/// \param uiFlags
	///   flags controlling the detailed behavior of this function. See VFileSystemFlags.
	/// \return
	///   whether adding the file system was successful
	/// \see VFileAccessManager::SetRoot
	AEBOOL32 AddFileSystem( const char *lpRootName, AEFileSystemI *lpFileSystem, AEUINT32 uiFlags = 0 );

	/// \brief
	///   Removes a file system root.
	///
	/// This function operates on the global VFileAccessManager.
	///
	/// \param szRootName
	///   name of the file system root to remove
	void RemoveFileSystem( const char *lpRootName );

	/// \brief
	///   Removes all installed file systems from the global file access manager.
	///
	/// Along with the file systems, all search paths are removed as well, since they are not
	/// useful without their underlying file system.
	void RemoveAllFileSystems( void );

	/// \brief
	///   Adds a search path to be used by the engine for file operations.
	///
	/// This function operates on the global VFileAccessManager.
	///
	/// All search paths must be specified as absolute paths, starting with the a colon (:) followed
	/// by the root name of an installed file system, and optionally followed by a path below that
	/// root.
	///
	/// A valid search path might look like this:
	/// \verbatim :havok_sdk/Data/Vision/Base \endverbatim
	///
	/// \param szPath
	///   the search path to add
	/// \param uiFlags
	///   flags controlling the detailed behavior of this function. See VSearchPathFlags.
	/// \return
	///   the index of the added or existing search path, or \c -1, if the search path did
	///   not exist and could not be added
	/// \see VFileAccessManager::AddSearchPath
	AEINT32 AddSearchPath( const char *lpPath, AEUINT32 uiFlags = 0 );

	/// \brief
	///   Removes a specific search path.
	/// \param szPath
	///   the path to remove. This path must specified in the same format as for AddSearchPath().
	void RemoveSearchPath( const char *lpPath );

	/// \brief
	///   Removes all search paths form the global file access manager.
	void RemoveAllSearchPaths( void );

	/// \brief
	///   Returns the number of search paths registered with the global file access manager.
	AEUINT32 GetNumSearchPaths( void ) const;

	/// \brief
	///   Returns the base path of a search path.
	/// \param uiIndex
	///   the index of the search path whose base path to get
	/// \return
	///   the base path of the search path, or \c NULL, if \c uiIndex is not valid
	const char *GetSearchPath( AEUINT32 uiIndex ) const;

	/// @}


	/// @name File Handling
	/// @{

	/// \brief
	///   Opens a file for reading.
	/// \param szPath
	///   path of the file to open. This can be a native, an absolute, or a relative path.
	/// \param uiOpenFlags
	///   flags to influence the behavior of this function; see VFileAccessManager::OpenFlags
	/// \return
	///   a stream to read from the file if successful; \c NULL otherwise
	AEFileInStreamI *Open( const char *lpPath, AEUINT32 uiOpenFlags = 0 );

	/// \brief
	///   Opens a file for writing. If the file doesn't exist, it is created.
	/// \param szPath
	///   path of the file to open/create. This can be a native, an absolute, or a relative path.
	/// \param uiCreateFlags
	///   flags to influence the behavior of this function; see VFileAccessManager::CreateFlags
	/// \return
	///   a stream to write to the file if successful; \c NULL otherwise
	AEFileOutStreamI *Create( const char *lpPath, AEUINT32 uiCreateFlags = 0 );

	/// \brief
	///   Removes (deletes) a file.
	/// \param szPath
	///   path of the file to remove. This can be a native, an absolute, or a relative path.
	/// \param uiRemoveFlags
	///   flags to influence the behavior of this function; see VFileAccessManager::RemoveFlags
	/// \return
	///   a FileRemoveResult return code
	AEUINT32 Remove( const char *lpPath, AEUINT32 uiRemoveFlags = 0 );

	/// \brief
	///   Checks whether a file exists.
	/// \param szPath
	///   path of the file to check. This can be a native, an absolute, or a relative path.
	/// \return
	///   \c true if the file exists; \c false if an error occurred during look-up or the file 
	///   doesn't exist
	AEBOOL32 FileExists( const char *lpPath );

	/// \brief
	///   Checks whether a directory exists.
	/// \param szPath
	///   path of the directory to check. This can be a native, an absolute, or a relative path.
	/// \return
	///   \c true if the directory exists; \c false if an error occurred during look-up or the directory 
	///   doesn't exist
	AEBOOL32 DirectoryExists( const char *lpPath );

	/// \brief
	///   Retrieves the timestamp of a file (usually the last-modified time).
	/// \param szPath
	///   path of the file to retrieve the timestamp of. This can be a native, an absolute, or a 
	///   relative path.
	/// \param destTime
	///   receives the timestamp value
	/// \return
	///   whether the time stamp could be successfully retrieved
	AEBOOL32 GetFileTimeStamp( const char *lpPath, AEDateTime &dtDestTime );

	/// @}
};
#endif // _AEFILEMANAGER_H_

